"""
@Author：WangYuXiang
@E-mile：Hill@3io.cc
@CreateTime：2021/4/22 14:47
@DependencyLibrary：无
@MainFunction：无
@FileDoc： 
    models.py
    文件说明
@ChangeHistory:
    datetime action why
    example:
    2021/4/22 14:47 change 'Fix bug'
        
"""
import os
from datetime import datetime
from enum import IntEnum

import aiofiles
from PIL import Image
from sanic import Sanic
from sanic.request import File
from tortoise import fields

from common.utils.models import BaseModel
from settings import APP_NAME

PDF_FILE_TYPES = ('pdf',)
IMAGE_FILE_TYPES = ('jpg', 'jpeg', 'png', 'webp', 'gif',)
VIDEOS_FILE_TYPES = ('mp4', 'avi', 'mov', 'mkv',)
AUDIO_FILE_TYPES = ('mp3',)
EXECUTABLE_FILE_TYPES = ('exe', 'sh', 'com', 'bat', 'vbs', 'cmd')
DOCUMENT_FILE_TYPES = ('txt', 'md',)
OFFICE_FILE_TYPES = ('xlsx', 'xls', 'doc', 'docx',)


class FileType(IntEnum):
    OTHER = 0
    PDF = 1
    IMAGE = 2
    VIDEOS = 3
    AUDIO = 4
    EXE = 5
    DOCUMENT = 6
    OFFICE = 7


class AccessoryModel(BaseModel):
    """附件表"""
    file_name = fields.CharField(512, description='文件名')
    file_original_name = fields.CharField(512, description='文件原始名')
    file_path = fields.CharField(512, description='文件存储路径')
    file_suffix = fields.CharField(10, description='文件后缀')
    file_size = fields.IntField(description='文件大小/KB')
    file_width = fields.IntField(null=True, description='文件宽')
    file_height = fields.IntField(null=True, description='文件高')
    file_type = fields.IntEnumField(
        enum_type=FileType, null=True, default=5, description='文件类型')
    file_note = fields.CharField(512, null=True, description='文件备注')
    # TODO 尚未适配网络路径
    is_net = fields.BooleanField(default=False, description='是否是网络路径')
    file_key = fields.CharField(512, null=True, description='网络路径唯一KEY')

    @staticmethod
    async def request_file_save_to_local(request, file: File, file_name=None, file_note=None) -> "AccessoryModel":
        save_path = request.app.config.UPLOAD_PATH
        file_original_name = file.name
        if '.' not in file_original_name:
            raise TypeError('文件名必须包含后缀名')
        stream = file.body
        file_original_name_split = file_original_name.split('.')
        file_suffix = file_original_name_split[-1].lower()
        if file_name is None:
            file_name = file_original_name[:-(len(file_suffix) + 1)]
        file_height = 0
        file_width = 0
        file_type = AccessoryModel.get_file_type(file_suffix)
        now = datetime.now()
        save_path = os.path.join(save_path, str(
            now.year), str(now.month), str(now.day))
        if not os.path.exists(save_path):
            os.makedirs(save_path)
        save_path = os.path.join(save_path, '{}.{}'.format(
            file_original_name, file_suffix))
        await AccessoryModel.save_file(stream, save_path)
        if file_suffix in IMAGE_FILE_TYPES:
            file_height, file_width = AccessoryModel.get_image_wh(save_path)
        file_size = len(stream)
        accessory = AccessoryModel(**{
            'file_name': file_name,
            'file_original_name': file_original_name,
            'file_path': save_path,
            'file_suffix': file_suffix,
            'file_size': file_size,
            'file_width': file_width,
            'file_height': file_height,
            'file_type': file_type,
            'file_note': file_note,
        })
        await accessory.save()
        return accessory

    async def change_file_to_loacl(self, request, file: File):
        save_path = request.app.config.UPLOAD_PATH
        file_original_name = file.name
        if '.' not in file_original_name:
            raise TypeError('文件名必须包含后缀名')
        stream = file.body
        file_original_name_split = file_original_name.split('.')
        file_suffix = file_original_name_split[-1].lower()
        file_height = 0
        file_width = 0
        file_type = AccessoryModel.get_file_type(file_suffix)
        now = datetime.now()
        save_path = os.path.join(save_path, str(
            now.year), str(now.month), str(now.day))
        if not os.path.exists(save_path):
            os.makedirs(save_path)
        save_path = os.path.join(save_path, '{}.{}'.format(
            file_original_name, file_suffix))
        await AccessoryModel.save_file(stream, save_path)
        if file_suffix in IMAGE_FILE_TYPES:
            file_height, file_width = AccessoryModel.get_image_wh(save_path)
        file_size = len(stream)
        if os.path.exists(self.file_path):
            os.remove(self.file_path)

        self.file_original_name = file_original_name
        self.file_path = save_path
        self.file_width = file_width
        self.file_height = file_height
        self.file_type = file_type
        self.file_suffix = file_suffix
        self.file_size = file_size
        await self.save()
        return self

    @staticmethod
    def get_image_wh(save_path):
        img = Image.open(save_path)
        img.save(save_path, optimize=True, quality=80)
        return img.height, img.width

    @staticmethod
    async def save_file(stream, save_path):
        async with aiofiles.open(save_path, 'wb') as f:
            await f.write(stream)

    def get_download_url(self):
        """得到下载地址"""
        return Sanic.get_app(APP_NAME).url_for('accessory.accessory_local-detail', pk=self.pk)

    @staticmethod
    def get_file_type(file_suffix: str):
        """
        通过文件尾缀得到类型
        :param file_suffix: 尾缀
        :return:
        """
        if file_suffix in PDF_FILE_TYPES:
            return FileType.PDF
        elif file_suffix in IMAGE_FILE_TYPES:
            return FileType.IMAGE
        elif file_suffix in VIDEOS_FILE_TYPES:
            return FileType.VIDEOS
        elif file_suffix in AUDIO_FILE_TYPES:
            return FileType.AUDIO
        elif file_suffix in EXECUTABLE_FILE_TYPES:
            return FileType.EXE
        elif file_suffix in DOCUMENT_FILE_TYPES:
            return FileType.DOCUMENT
        elif file_suffix in OFFICE_FILE_TYPES:
            return FileType.OFFICE
        else:
            return FileType.OTHER
